package com.feng.fengDUtil.dao;

import static com.feng.fengDUtil.dao.pojo.SqlLevelEnum.SHOW_EXECUTE_SQL;

import com.feng.fengDUtil.dao.converter.DateConverter;
import com.feng.fengDUtil.dao.converter.impl.DateDefaultConverter;
import com.feng.fengDUtil.dao.pojo.SqlLevelEnum;
import java.sql.Connection;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.lang3.time.DateFormatUtils;

public class SqlExecutor {

  private static final Logger log = Logger.getLogger(SqlExecutor.class.getName());
  private static boolean sqlCheck = false;
  private static SqlLevelEnum showLevel = SHOW_EXECUTE_SQL;
  private String sql;
  private DateConverter converter;
  private long start = 0L;
  private ConnectionManager connectionManager;

  public SqlExecutor(ConnectionManager connectionManager, String sql) {
    this.connectionManager = connectionManager;
    this.sql = DbHelp.filterSql(sql);
    if (sqlCheck) {
      checkSql();
    }
  }

  public static SqlLevelEnum getShowLevel() {
    return showLevel;
  }

  public static void setShowLevel(SqlLevelEnum showLevel) {
    SqlExecutor.showLevel = showLevel;
  }

  private void checkSql() {
    if (sql.indexOf("？") != -1) {
      throw new RuntimeException("sql语句中含有中文的问号！！！！");
    }
  }

  private void printSqlAndParamsIfNecessary(Object... params) {
    switch (showLevel) {
      case SHOW_EXECUTE_SQL:
        printSqlWithParam(params);
        start = System.currentTimeMillis();
        break;
      case SHOW_SQL_ONLY:
        start = System.currentTimeMillis();
        log.info("执行sql：" + sql);
        break;
      default:
    }
  }

  private void printSqlWithParam(Object... params) {
    String quoteSql = sql;
    for (Object param : params) {
      if (param == null) {
        quoteSql = quoteSql.replaceFirst("\\?", "null");
        continue;
      }
      if (param instanceof String) {
        quoteSql = quoteSql.replaceFirst("\\?", "'" + param + "'");
      } else if ((param instanceof Number || param instanceof Enum)) {
        quoteSql = quoteSql.replaceFirst("\\?", String.valueOf(param));
      } else if (param instanceof Date) {
        quoteSql = quoteSql.replaceFirst("\\?",
            "'" + DateFormatUtils.format((Date) param, "yyyy-MM-dd HH:mm:ss") + "'");
      }
    }
    log.info("标准sql:" + quoteSql);

        /*if ((params != null) && (params.length != 0)) {
            System.out.print("jdbc sql参数为：{");

            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i]);
                if (i != params.length - 1) {
                    System.out.print(",");
                }
            }

            System.out.println("}");
        } else {
            System.out.print("jdbc sql参数为：{}");
        }*/
  }

  private Object query(boolean many, Class clazz, Object... params) {
    return queryByResultSetHandler(ResultSetHandlerHelp.getResultSetHandler(many, clazz), params);
  }

  public Object queryByResultSetHandler(ResultSetHandler rsh, Object... params) {
    boolean exception = false;
    Object ret;
    try {
      Connection conn = connectionManager.getConnectionOrNewFromDataSoureIfNull();
      printSqlAndParamsIfNecessary(params);
      ret = new QueryRunner().query(conn, sql, rsh, params);
      printElapseTimeIfNecessary();
    } catch (Throwable e) {
      exception = true;
      e.printStackTrace();
      throw new RuntimeException(e);
    } finally {
      connectionManager.closeConnectionByException(exception);
    }
    return ret;
  }

  private void printElapseTimeIfNecessary() {
    if (start > 0L) {
      log.info("执行jdbc sql耗时:[" + (System.currentTimeMillis() - start) + " ]毫秒.");
    }
  }


  public <T> T one(Class<T> clazz, Object... params) {
    return (T) queryByResultSetHandler(ResultSetHandlerHelp.getResultSetHandler(false, clazz),
        params);
  }

  public <T> List<T> many(Class<T> clazz, Object... params) {
    return (List) query(true, clazz, params);
  }

  public <T> List<T> list(Class<T> clazz, Object... params) {
    return (List) query(true, clazz, params);
  }

  public int update(Object... params) {
    boolean exception = false;
    try {
      Connection conn = connectionManager.getConnectionOrNewFromDataSoureIfNull();
      printSqlAndParamsIfNecessary(params);
      convertDateIfNecessary(params);
      int ret = new QueryRunner().update(conn, sql, params);
      connectionManager.closeConnectionByException(exception);
      printElapseTimeIfNecessary();
      return ret;
    } catch (Exception e) {
      e.printStackTrace();
      exception = true;
      connectionManager.closeConnectionByException(exception);
      throw new RuntimeException(e);
    }
  }

  public <T> T insert(Class<T> clazz, Object[] params) {
    boolean exception = false;
    try {
      Connection conn = connectionManager.getConnectionOrNewFromDataSoureIfNull();
      printSqlAndParamsIfNecessary(params);
      convertDateIfNecessary(params);
      T ret = (T) new QueryRunner()
          .insert(conn, sql, ResultSetHandlerHelp.getResultSetHandler(false, clazz), params);
      connectionManager.closeConnectionByException(exception);
      printElapseTimeIfNecessary();
      return ret;
    } catch (Exception e) {
      e.printStackTrace();
      exception = true;
      connectionManager.closeConnectionByException(exception);
      throw new RuntimeException(e);
    }
  }

  public <T> List<T> insertBatch(Class<T> clazz, Object[][] params) {
    boolean exception = false;
    try {
      Connection conn = connectionManager.getConnectionOrNewFromDataSoureIfNull();
      printSqlAndParamsIfNecessary(params);
      List<T> ret = (List<T>) new QueryRunner()
          .insertBatch(conn, sql, ResultSetHandlerHelp.getResultSetHandler(true, clazz),
              params);
      connectionManager.closeConnectionByException(exception);
      printElapseTimeIfNecessary();
      return ret;
    } catch (Exception e) {
      e.printStackTrace();
      exception = true;
      connectionManager.closeConnectionByException(exception);
      throw new RuntimeException(e);
    }
  }

  private void convertDateIfNecessary(Object[] params) {
    if (params != null) {
      for (int i = 0; i < params.length; i++) {
        Object object = params[i];
        if ((object != null) && ((object instanceof java.util.Date))
            && (!(object instanceof java.sql.Date)) && (!(object instanceof Time))
            && (!(object instanceof Timestamp))) {
          if (converter == null) {
            converter = new DateDefaultConverter();
          }

          params[i] = converter.convertDate((java.util.Date) object);
        }
      }
    }
  }

  public int[] batch(Object[][] params) {
    try {
      Connection conn = connectionManager.getConnectionOrNewFromDataSoureIfNull();
      for (int i = 0; i < params.length; i++) {
        printSqlAndParamsIfNecessary(params[i]);
      }
      int[] ret = new QueryRunner().batch(conn, sql, params);
      printElapseTimeIfNecessary();
      return ret;
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }

  public void setConverter(DateConverter converter) {
    this.converter = converter;
  }
}